#include "StdAfx.hpp"
#include "Multishader.hpp"
#include "D3dResources.hpp"
#include "Engine.hpp"

using namespace common;


//HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
// Multishader

Multishader::Multishader(
	const string &FileName, const string &CacheFileNameMask,
	const char * const MacroNames[], const uint MacroBitCounts[], uint MacroCount)
: m_FileName(FileName)
, m_CacheFileNameMask(CacheFileNameMask)
, m_CacheFileNameSpecialCharPos(string::npos)
, m_MacroNames(MacroCount)
, m_MacroBitCounts(MacroCount)
{
	assert(CacheFileNameMask.find('*') != string::npos);

	for (uint i = 0; i < MacroCount; i++)
	{
		m_MacroNames[i] = MacroNames[i];
		m_MacroBitCounts[i] = MacroBitCounts[i];
	}

	if (!m_CacheFileNameMask.empty())
	{
		m_CacheFileNameSpecialCharPos = m_CacheFileNameMask.find('*');
		assert(m_CacheFileNameSpecialCharPos != string::npos);
	}
}

Multishader::~Multishader()
{
	Clear();
}

void Multishader::Clear()
{
	for (MAP_TYPE::iterator it = m_Map.begin(); it != m_Map.end(); ++it)
		it->second->Release();
	m_Map.clear();
}

D3dEffect * Multishader::Get(const uint *MacroValues, uint Flags)
{
	uint Hash = CalcHash(MacroValues);
	MAP_TYPE::iterator it = m_Map.find(Hash);
	if (it != m_Map.end())
	{
		if ((Flags & FLAG_LOAD) != 0)
			it->second->Load();
		else if ((Flags & FLAG_BEGIN_LOADING) != 0)
			it->second->BeginLoading();
		return it->second;
	}

	string HashStr; UintToStr2(&HashStr, Hash, 8, 16);

	string CacheFileName = m_CacheFileNameMask;
	if (!CacheFileName.empty())
	{
		CacheFileName.erase(m_CacheFileNameSpecialCharPos, 1);
		CacheFileName.insert(m_CacheFileNameSpecialCharPos, HashStr);
	}

	D3dEffect *eff = new D3dEffect(m_FileName, CacheFileName);
	for (uint i = 0; i < m_MacroNames.size(); i++)
		eff->AddMacro(m_MacroNames[i], MacroValues[i]);
	eff->EnableFileChangeMonitoring(ResourceObject::STATE_UNLOADED);

	m_Map.insert(MAP_TYPE::value_type(Hash, eff));

	if ((Flags & FLAG_LOAD) != 0)
		eff->Load();
	else if ((Flags & FLAG_BEGIN_LOADING) != 0)
		eff->BeginLoading();
	return eff;
}

ID3DXEffect * Multishader::GetEffect(const uint *MacroValues, uint Flags)
{
	D3dEffect *Res = Get(MacroValues, Flags);
	return (Res != NULL) ? Res->GetEffect() : NULL;
}

uint Multishader::CalcHash(const uint *MacroValues)
{
	uint BitIndex = 0, Hash = 0;
	for (uint i = 0; i < m_MacroNames.size(); i++)
	{
		Hash |= MacroValues[i] << BitIndex;
		BitIndex += m_MacroBitCounts[i];
	}
	assert(BitIndex <= 32);
	return Hash;
}

Multishader * Multishader::GetOrCreate( const string &name, const string &FileName, const string &CacheFileNameMask, const char * const MacroNames[], const uint MacroBitCounts[], uint MacroCount )
{
	Multishader *m = g_Engine->FindObjectByName_T<Multishader>(name);
	
	if (m)
		m->AddRef();
	else
	{
		m = new Multishader(FileName, CacheFileNameMask, MacroNames, MacroBitCounts, MacroCount);
		m->SetName(name);
	}

	return m;
}
